#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
花生tg群发群发软件
使用Bot Token，无需手机号码
"""

import asyncio
import json
import os
import sys
from datetime import datetime
from typing import List, Dict, Optional
import logging

# Telegram Bot相关库
from telethon import TelegramClient
from telethon.tl.types import User, Chat, Channel
from telethon.errors import FloodWaitError

# 用户界面相关
import tkinter as tk
from tkinter import ttk, messagebox, filedialog, scrolledtext
import threading

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('telegram_bot_sender.log', encoding='utf-8'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

class TelegramBotSender:
    """Telegram Bot群发软件主类"""
    
    def __init__(self):
        # Telegram Bot配置
        self.api_id = 29668108
        self.api_hash = "578251ccac8cb1c47b91721c4bad11a2"
        self.bot_token = ""  # 需要用户输入
        self.client = None
        self.is_connected = False
        
        # 数据存储
        self.contacts = []
        self.groups = []
        self.sent_messages = []
        
        # 创建GUI
        self.create_gui()
        
    def create_gui(self):
        """创建图形用户界面"""
        self.root = tk.Tk()
        self.root.title("Telegram Bot群发软件")
        self.root.geometry("800x650")
        self.root.resizable(True, True)
        
        # 设置样式
        style = ttk.Style()
        style.theme_use('clam')
        
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 配置网格权重
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=1)
        main_frame.rowconfigure(4, weight=1)
        
        # Bot Token配置
        token_frame = ttk.LabelFrame(main_frame, text="Bot Token配置", padding="5")
        token_frame.grid(row=0, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
        token_frame.columnconfigure(1, weight=1)
        
        ttk.Label(token_frame, text="Bot Token:").grid(row=0, column=0, sticky=tk.W, pady=5)
        self.token_var = tk.StringVar()
        self.token_entry = ttk.Entry(token_frame, textvariable=self.token_var, width=50, show="*")
        self.token_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=5, pady=5)
        
        ttk.Button(token_frame, text="连接Bot", command=self.connect_bot).grid(row=0, column=2, padx=5, pady=5)
        
        # 连接状态
        ttk.Label(main_frame, text="连接状态:").grid(row=1, column=0, sticky=tk.W, pady=5)
        self.status_label = ttk.Label(main_frame, text="未连接", foreground="red")
        self.status_label.grid(row=1, column=1, sticky=tk.W, pady=5)
        
        # 联系人管理
        contacts_frame = ttk.LabelFrame(main_frame, text="联系人管理", padding="5")
        contacts_frame.grid(row=2, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
        contacts_frame.columnconfigure(1, weight=1)
        
        ttk.Button(contacts_frame, text="获取群组", command=self.get_groups).grid(row=0, column=0, padx=5)
        ttk.Button(contacts_frame, text="导入联系人", command=self.import_contacts).grid(row=0, column=1, padx=5)
        ttk.Button(contacts_frame, text="导出联系人", command=self.export_contacts).grid(row=0, column=2, padx=5)
        
        # 消息发送
        message_frame = ttk.LabelFrame(main_frame, text="消息发送", padding="5")
        message_frame.grid(row=3, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
        message_frame.columnconfigure(1, weight=1)
        
        ttk.Label(message_frame, text="消息内容:").grid(row=0, column=0, sticky=tk.W, pady=5)
        self.message_text = scrolledtext.ScrolledText(message_frame, height=4, width=60)
        self.message_text.grid(row=0, column=1, columnspan=2, sticky=(tk.W, tk.E), pady=5)
        
        ttk.Label(message_frame, text="发送间隔(秒):").grid(row=1, column=0, sticky=tk.W, pady=5)
        self.interval_var = tk.StringVar(value="2")
        ttk.Entry(message_frame, textvariable=self.interval_var, width=10).grid(row=1, column=1, sticky=tk.W, pady=5)
        
        ttk.Button(message_frame, text="发送消息", command=self.send_messages).grid(row=1, column=2, padx=5, pady=5)
        
        # 联系人列表
        list_frame = ttk.LabelFrame(main_frame, text="联系人列表", padding="5")
        list_frame.grid(row=4, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
        list_frame.columnconfigure(0, weight=1)
        list_frame.rowconfigure(0, weight=1)
        
        # 创建Treeview
        columns = ('name', 'username', 'id', 'type')
        self.contact_tree = ttk.Treeview(list_frame, columns=columns, show='headings', height=15)
        
        # 设置列标题
        self.contact_tree.heading('name', text='名称')
        self.contact_tree.heading('username', text='用户名')
        self.contact_tree.heading('id', text='ID')
        self.contact_tree.heading('type', text='类型')
        
        # 设置列宽
        self.contact_tree.column('name', width=200)
        self.contact_tree.column('username', width=150)
        self.contact_tree.column('id', width=100)
        self.contact_tree.column('type', width=80)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.contact_tree.yview)
        self.contact_tree.configure(yscrollcommand=scrollbar.set)
        
        self.contact_tree.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
        
        # 右键菜单
        self.context_menu = tk.Menu(self.root, tearoff=0)
        self.context_menu.add_command(label="删除选中", command=self.remove_selected)
        self.context_menu.add_command(label="全选", command=self.select_all)
        self.context_menu.add_command(label="取消全选", command=self.deselect_all)
        
        self.contact_tree.bind("<Button-3>", self.show_context_menu)
        
        # 日志显示
        log_frame = ttk.LabelFrame(main_frame, text="操作日志", padding="5")
        log_frame.grid(row=5, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
        log_frame.columnconfigure(0, weight=1)
        
        self.log_text = scrolledtext.ScrolledText(log_frame, height=6, width=80)
        self.log_text.grid(row=0, column=0, sticky=(tk.W, tk.E))
        
    def log_message(self, message: str):
        """添加日志消息"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        log_entry = f"[{timestamp}] {message}\n"
        self.log_text.insert(tk.END, log_entry)
        self.log_text.see(tk.END)
        logger.info(message)
        
    def show_context_menu(self, event):
        """显示右键菜单"""
        self.context_menu.post(event.x_root, event.y_root)
        
    def connect_bot(self):
        """连接Telegram Bot"""
        bot_token = self.token_var.get().strip()
        if not bot_token:
            messagebox.showerror("错误", "请输入Bot Token")
            return
            
        def connect_async():
            try:
                self.log_message("正在连接Telegram Bot...")
                self.client = TelegramClient('bot_session', self.api_id, self.api_hash)
                
                # 创建新的事件循环
                loop = asyncio.new_event_loop()
                asyncio.set_event_loop(loop)
                
                try:
                    # 启动Bot客户端
                    loop.run_until_complete(self.client.start(bot_token=bot_token))
                    
                    if self.client.is_connected():
                        self.is_connected = True
                        self.bot_token = bot_token
                        self.root.after(0, lambda: self.status_label.config(text="已连接", foreground="green"))
                        self.root.after(0, lambda: self.log_message("Telegram Bot连接成功"))
                        
                        # 获取Bot信息
                        me = loop.run_until_complete(self.client.get_me())
                        self.root.after(0, lambda: self.log_message(f"Bot名称: {me.first_name} (@{me.username})"))
                    else:
                        self.root.after(0, lambda: self.log_message("连接失败"))
                        
                except Exception as auth_error:
                    error_msg = str(auth_error)
                    if "bot token" in error_msg.lower():
                        self.root.after(0, lambda: self.log_message("错误: Bot Token无效"))
                    else:
                        self.root.after(0, lambda: self.log_message(f"连接错误: {error_msg}"))
                finally:
                    loop.close()
                    
            except Exception as error:
                error_msg = str(error)
                self.root.after(0, lambda: self.log_message(f"连接错误: {error_msg}"))
                
        threading.Thread(target=connect_async, daemon=True).start()
        
    def get_groups(self):
        """获取群组列表"""
        if not self.is_connected:
            messagebox.showerror("错误", "请先连接Bot")
            return
            
        def get_groups_async():
            try:
                self.log_message("正在获取群组...")
                
                # 创建新的事件循环
                loop = asyncio.new_event_loop()
                asyncio.set_event_loop(loop)
                
                try:
                    async def fetch_groups():
                        groups = []
                        async for dialog in self.client.iter_dialogs():
                            if dialog.is_group or dialog.is_channel:
                                groups.append({
                                    'id': dialog.id,
                                    'name': dialog.title,
                                    'username': getattr(dialog.entity, 'username', '') or '',
                                    'type': '群组' if dialog.is_group else '频道'
                                })
                        return groups
                    
                    groups = loop.run_until_complete(fetch_groups())
                    
                    self.groups = groups
                    self.root.after(0, self.update_contact_list)
                    self.root.after(0, lambda: self.log_message(f"获取到 {len(groups)} 个群组"))
                finally:
                    loop.close()
                
            except Exception as error:
                error_msg = str(error)
                self.root.after(0, lambda: self.log_message(f"获取群组错误: {error_msg}"))
                
        threading.Thread(target=get_groups_async, daemon=True).start()
        
    def update_contact_list(self):
        """更新联系人列表显示"""
        # 清空现有项目
        for item in self.contact_tree.get_children():
            self.contact_tree.delete(item)
            
        # 添加群组
        for group in self.groups:
            self.contact_tree.insert('', 'end', values=(
                group['name'],
                group['username'],
                group['id'],
                group['type']
            ))
            
    def import_contacts(self):
        """导入联系人"""
        filename = filedialog.askopenfilename(
            title="选择联系人文件",
            filetypes=[("JSON files", "*.json"), ("All files", "*.*")]
        )
        
        if filename:
            try:
                with open(filename, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    
                if isinstance(data, list):
                    self.groups.extend(data)
                    self.update_contact_list()
                    self.log_message(f"成功导入 {len(data)} 个联系人")
                else:
                    messagebox.showerror("错误", "文件格式不正确")
                    
            except Exception as e:
                messagebox.showerror("错误", f"导入失败: {str(e)}")
                
    def export_contacts(self):
        """导出联系人"""
        filename = filedialog.asksaveasfilename(
            title="保存联系人文件",
            defaultextension=".json",
            filetypes=[("JSON files", "*.json"), ("All files", "*.*")]
        )
        
        if filename:
            try:
                with open(filename, 'w', encoding='utf-8') as f:
                    json.dump(self.groups, f, ensure_ascii=False, indent=2)
                    
                self.log_message(f"成功导出 {len(self.groups)} 个联系人")
                
            except Exception as e:
                messagebox.showerror("错误", f"导出失败: {str(e)}")
                
    def remove_selected(self):
        """删除选中的联系人"""
        selected_items = self.contact_tree.selection()
        if not selected_items:
            messagebox.showwarning("警告", "请先选择要删除的联系人")
            return
            
        if messagebox.askyesno("确认", f"确定要删除选中的 {len(selected_items)} 个联系人吗？"):
            for item in selected_items:
                values = self.contact_tree.item(item)['values']
                # 从列表中移除
                self.groups = [g for g in self.groups if not (
                    g['name'] == values[0] and g['id'] == values[2]
                )]
                
            self.update_contact_list()
            self.log_message(f"删除了 {len(selected_items)} 个联系人")
            
    def select_all(self):
        """全选"""
        for item in self.contact_tree.get_children():
            self.contact_tree.selection_add(item)
            
    def deselect_all(self):
        """取消全选"""
        self.contact_tree.selection_remove(self.contact_tree.selection())
        
    def send_messages(self):
        """发送消息"""
        if not self.is_connected:
            messagebox.showerror("错误", "请先连接Bot")
            return
            
        selected_items = self.contact_tree.selection()
        if not selected_items:
            messagebox.showwarning("警告", "请先选择要发送消息的群组")
            return
            
        message = self.message_text.get("1.0", tk.END).strip()
        if not message:
            messagebox.showwarning("警告", "请输入要发送的消息")
            return
            
        try:
            interval = float(self.interval_var.get())
        except ValueError:
            messagebox.showerror("错误", "发送间隔必须是数字")
            return
            
        def send_async():
            try:
                self.log_message(f"开始发送消息到 {len(selected_items)} 个群组")
                
                # 创建新的事件循环
                loop = asyncio.new_event_loop()
                asyncio.set_event_loop(loop)
                
                try:
                    async def send_messages():
                        sent_count = 0
                        failed_count = 0
                        
                        for item in selected_items:
                            values = self.contact_tree.item(item)['values']
                            name = values[0]
                            username = values[1]
                            group_id = values[2]
                            
                            try:
                                # 发送消息到群组
                                await self.client.send_message(int(group_id), message)
                                sent_count += 1
                                self.root.after(0, lambda: self.log_message(f"成功发送到: {name}"))
                                
                                # 等待间隔时间
                                await asyncio.sleep(interval)
                                
                            except FloodWaitError as flood_error:
                                wait_time = flood_error.seconds
                                self.root.after(0, lambda: self.log_message(f"遇到限制，等待 {wait_time} 秒"))
                                await asyncio.sleep(wait_time)
                                continue
                            except Exception as send_error:
                                failed_count += 1
                                error_msg = str(send_error)
                                self.root.after(0, lambda: self.log_message(f"发送失败 {name}: {error_msg}"))
                                
                        self.root.after(0, lambda: self.log_message(f"发送完成: 成功 {sent_count}, 失败 {failed_count}"))
                    
                    loop.run_until_complete(send_messages())
                finally:
                    loop.close()
                
            except Exception as error:
                error_msg = str(error)
                self.root.after(0, lambda: self.log_message(f"发送消息错误: {error_msg}"))
                
        threading.Thread(target=send_async, daemon=True).start()
        
    def run(self):
        """运行应用程序"""
        self.root.mainloop()
        
    def cleanup(self):
        """清理资源"""
        if self.client and self.is_connected:
            try:
                loop = asyncio.get_event_loop()
                loop.run_until_complete(self.client.disconnect())
            except:
                pass

def main():
    """主函数"""
    app = TelegramBotSender()
    try:
        app.run()
    finally:
        app.cleanup()

if __name__ == "__main__":
    main() 